package nbcp.http

import nbcp.base.comm.config
import nbcp.base.extend.HasValue
import nbcp.base.extend.ToJson
import nbcp.base.extend.ToMap
import nbcp.base.utils.ClassUtil
import nbcp.base.utils.ProxyUtil
import nbcp.base.utils.ReflectUtil
import nbcp.base.utils.SpringUtil
import org.slf4j.LoggerFactory
import org.springframework.beans.BeansException
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.context.annotation.EnableAspectJAutoProxy
import java.lang.reflect.Proxy
import java.util.*

@EnableAspectJAutoProxy(exposeProxy = true)
class HttpClientApplicationInitializer : BeanDefinitionRegistryPostProcessor {

    private fun isMatch(onProperty: ConditionalOnProperty): Boolean {
        //https://www.cnblogs.com/yanliang12138/p/13283395.html
        var configValue = "";
        for (it in onProperty.value) {
            var key = it;
            if (onProperty.prefix.HasValue) {
                key = onProperty.prefix + "." + key;
            }

            var v = SpringUtil.context.environment.getProperty(key)
            if (v.HasValue) {
                configValue = v;
                break;
            }
        }

        if (onProperty.matchIfMissing) {
            if (configValue.isNullOrEmpty()) {
                return false;
            }
        } else {
            if (configValue.isNullOrEmpty()) {
                return false;
            } else {
                if (onProperty.havingValue.HasValue && onProperty.havingValue != configValue) {
                    return false;
                }
            }
        }

        return true
    }

    @Throws(BeansException::class)
    override fun postProcessBeanDefinitionRegistry(registry: BeanDefinitionRegistry) {
        val url = mapDefine
        val httpClients = startupHttpClients
        for (httpClient in httpClients) {
            val onProperty = httpClient.getAnnotation(ConditionalOnProperty::class.java)
            if (onProperty != null) {
                if (!isMatch(onProperty)) {
                    continue
                }
            }

            val httpClientInvocation = HttpClientInvocation(url, httpClient)
            val proxy = ProxyUtil.jdkProxy(httpClient, httpClientInvocation)
            val beanName = httpClient.name + "HttpProxy"
            val existsBeans = SpringUtil.context.getBeansOfType(httpClient).values
            if (existsBeans.isNotEmpty()) {
                val existBean =
                    Proxy.getInvocationHandler(existsBeans.elementAt(0).javaClass)
                val targetType =
                    ReflectUtil.getPrivatePropertyValue(existBean, "targetType").toString()
                throw RuntimeException(
                    java.lang.String.format(
                        "重复注册 HttpClient Bean！ {} - {}",
                        targetType,
                        httpClient.name
                    )
                )
            }
            try {
                SpringUtil.registerBeanDefinition(beanName, proxy)
            } catch (e: Throwable) {
                log.error("注册 HttpClient Bean 失败 : {}", httpClient.name)
                throw e
            }
            log.info("注册 HttpClient Bean: {}", httpClient.name)
        }
    }

    private val startupHttpClients: Set<Class<*>>
        private get() {
            val mainClass = config.springApplication!!
            val mainClassPackage =
                mainClass.mainApplicationClass.`package`.getName().split(".")
                    .take(2)
                    .joinToString(".")

            val pkgs = LinkedHashSet<String>()
            pkgs.add("nbcp")
            pkgs.add(mainClassPackage)
            val ret = LinkedHashSet<Class<*>>()
            for (pkg in pkgs) {
                ret.addAll(
                    ClassUtil.findClassesWithAnnotationType(
                        pkg,
                        false,
                        HttpClient::class.java
                    )
                )
            }
            return ret
        }
    private val mapDefine: Map<String, String>
        private get() {
            try {
                var map = (SpringUtil.binder.bindOrCreate(
                    "app.http-client.url",
                    MutableMap::class.java
                ) as Map<String, String>? ?: mapOf())
                    .filter { it.value.HasValue }
                    .ToMap({ it.key },
                        {
                            val str = it.value
                            if (str.startsWith("//")) {
                                return@ToMap "http:$str"
                            }
                            return@ToMap str
                        })

                log.info(map.ToJson())
                return map;
            } catch (e: Throwable) {
                log.error("忽略配置 app.http-client.url ! {}", e.message)
                return mapOf()
            }
        }


    override fun postProcessBeanFactory(beanFactory: ConfigurableListableBeanFactory) {
    }

    companion object {
        private val log = LoggerFactory.getLogger(this::class.java.declaringClass)
    }
}